package club.zhcs.mybatis.utils;

import static org.apache.ibatis.jdbc.SqlBuilder.AND;
import static org.apache.ibatis.jdbc.SqlBuilder.BEGIN;
import static org.apache.ibatis.jdbc.SqlBuilder.DELETE_FROM;
import static org.apache.ibatis.jdbc.SqlBuilder.FROM;
import static org.apache.ibatis.jdbc.SqlBuilder.INSERT_INTO;
import static org.apache.ibatis.jdbc.SqlBuilder.ORDER_BY;
import static org.apache.ibatis.jdbc.SqlBuilder.SELECT;
import static org.apache.ibatis.jdbc.SqlBuilder.SET;
import static org.apache.ibatis.jdbc.SqlBuilder.SQL;
import static org.apache.ibatis.jdbc.SqlBuilder.UPDATE;
import static org.apache.ibatis.jdbc.SqlBuilder.VALUES;
import static org.apache.ibatis.jdbc.SqlBuilder.WHERE;

import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import club.zhcs.mybatis.Entity;
import club.zhcs.mybatis.anno.Column;
import club.zhcs.mybatis.anno.Id;
import club.zhcs.mybatis.anno.Table;

/**
 * //TODO 使用SQL对象来处理
 * 
 * @author 王贵源
 * 
 *         create at 2014年10月8日 上午8:54:23
 */
@SuppressWarnings("deprecation")
public class SqlGener<T extends Entity> {
	private static final Logger log = Logger.getLogger(SqlGener.class);

	/**
	 * 获取id的属性
	 * 
	 * @param class1
	 *            类描述(字节码)
	 * @return id字段名称
	 */
	private String getIdFieldName(Class<? extends Entity> class1) {
		for (Field f : Classs.getAllfFields(class1)) {
			if (f.getAnnotation(Id.class) != null) {
				return f.getName();
			}
		}
		return "id";
	}

	/**
	 * 獲取id字段名
	 * 
	 * @param class1
	 * @return
	 */
	private String getIdColmnName(Class<? extends Entity> class1) {
		for (Field f : Classs.getAllfFields(class1)) {
			if (f.getAnnotation(Id.class) != null) {
				String id = f.getAnnotation(Id.class).value();
				return StringUtils.equals(id, "") ? f.getName() : id;
			}
		}
		return "id";
	}

	/**
	 * 生成insert语句
	 * 
	 * @param t
	 *            待插入对象
	 * @return 影响的记录条数(生成的id可以从实体对象上获取)
	 */
	public String insert(T t) {
		BEGIN();
		INSERT_INTO(t.tablename());

		for (Field f : Classs.getAllfFields(t.getClass())) {
			Column c = null;
			if ((c = f.getAnnotation(Column.class)) != null) {
				VALUES(c.value(), "#{" + f.getName() + "}");
			}
			if (f.getAnnotation(Id.class) != null) {
				VALUES(f.getName(), "#{" + f.getName() + "}");
			}
		}
		String target = SQL();
		log.debug(String.format("sql generated by SQLTemplate: \n {}", target));
		return target;
	}

	/**
	 * 更新,根据id进行更新
	 * 
	 * @param t
	 *            待更新对象
	 * @return
	 */
	public String update(T t) {
		BEGIN();
		UPDATE(t.tablename());
		for (Field f : Classs.getAllfFields(t.getClass())) {
			Column c = null;
			if ((c = f.getAnnotation(Column.class)) != null) {
				SET(c.value() + "=#{" + f.getName() + "}");
			}
			if (f.getAnnotation(Id.class) != null) {
				WHERE(f.getName() + "=#{" + f.getName() + "}");
			}
		}
		String target = SQL();
		log.debug(String.format("sql generated by SQLTemplate: \n {}", target));
		return target;
	}

	/**
	 * 更新非空字段
	 * 
	 * @param t
	 * @return
	 */
	public String updateNotNull(T t) {
		BEGIN();
		UPDATE(t.tablename());
		for (Field f : Classs.getAllfFields(t.getClass())) {
			Column c = null;
			if ((c = f.getAnnotation(Column.class)) != null) {
				Object obj = null;
				try {
					f.setAccessible(true);
					obj = f.get(t);
				} catch (IllegalArgumentException | IllegalAccessException e) {
					log.error(e.getMessage());
				}
				if (obj != null) {
					SET(c.value() + "=#{" + f.getName() + "}");
				}
			}
			if (f.getAnnotation(Id.class) != null) {
				WHERE(f.getName() + "=#{" + f.getName() + "}");
			}
		}
		String target = SQL();
		log.debug(String.format("sql generated by SQLTemplate: \n {}", target));
		return target;
	}

	/**
	 * 更新指定字段
	 * 
	 * @param paras
	 * @return
	 */
	public String updateFields(Map<String, Object> map) {
		T t = (T) map.get("entity");
		String[] fields = (String[]) map.get("fields");
		BEGIN();
		UPDATE(t.tablename());
		for (Field f : Classs.getAllfFields(t.getClass())) {
			// update chain
			Column c = null;
			if (contains(f.getName(), fields) && (c = f.getAnnotation(Column.class)) != null) {
				SET(c.value() + "=#{entity." + f.getName() + "}");
			}
			// update condition
			else if (f.getAnnotation(Id.class) != null) {
				WHERE(f.getName() + "=#{entity." + f.getName() + "}");
			}
		}
		String target = SQL();
		log.debug(String.format("sql generated by SQLTemplate: \n {}", target));
		return target;
	}

	/**
	 * 检查数组是否包含指定key
	 * 
	 * @param key
	 * @param pool
	 * @return
	 */
	private boolean contains(String key, String[] pool) {
		for (int i = 0; i < pool.length; i++) {
			if (StringUtils.equals(pool[i], key)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * count all
	 * 
	 * @param clazzOfT
	 * @return
	 */
	public String count(Class<T> clazzOfT) {
		BEGIN();
		SELECT("count(" + getIdColmnName(clazzOfT) + ")");
		FROM(clazzOfT.getAnnotation(Table.class).value());
		String target = SQL();
		log.debug(String.format("sql generated by SQLTemplate: \n {}", target));
		return target;
	}

	/**
	 * 删除
	 * 
	 * @param obj
	 *            待删除对象
	 * @return 删除操作影响的记录条数
	 */
	public String delete(T obj) {
		String idname = obj.id();
		BEGIN();
		DELETE_FROM(obj.tablename());
		WHERE(idname + "=#{" + getIdFieldName(obj.getClass()) + "}");
		String target = SQL();
		log.debug(String.format("sql generated by SQLTemplate: \n {}", target));
		return target;
	}

	/**
	 * 生成根据id查询语句
	 * 
	 * @param map
	 *            参数
	 * @return
	 */
	public String selectById(Map<String, Object> map) {
		Class<T> clazz = (Class<T>) map.get("clazz");
		Long id = (Long) map.get("id");
		BEGIN();
		SELECT("*");
		FROM(clazz.getAnnotation(Table.class).value());
		if (id != null) {
			WHERE(getIdColmnName(clazz) + " = #{id}");
		}
		String target = SQL();
		log.debug(String.format("sql generated by SQLTemplate: \n {}", target));
		return target;
	}

	/**
	 * 查询全部
	 * 
	 * @param clazzOfT
	 * @return
	 */
	public String select(Class<T> clazzOfT) {
		BEGIN();
		SELECT("*");
		FROM(clazzOfT.getAnnotation(Table.class).value());
		String target = SQL();
		log.debug(String.format("sql generated by SQLTemplate: \n {}", target));
		return target;
	}

	/**
	 * 排序查询
	 * 
	 * @param map
	 * @return
	 */
	public String selectWithOrder(Map<String, Object> map) {
		Class<T> clazz = (Class<T>) map.get("clazz");
		OrderBy[] orders = (OrderBy[]) map.get("orders");
		BEGIN();
		SELECT("*");
		FROM(clazz.getAnnotation(Table.class).value());
		for (OrderBy orderBy : orders) {
			ORDER_BY(orderBy.getCol() + " " + orderBy.getOrder());
		}
		String target = SQL();
		log.debug(String.format("sql generated by SQLTemplate: \n {}", target));
		return target;
	}

	/**
	 * 根据一组id查询
	 * 
	 * @param map
	 * @return
	 */
	public String selectByIds(Map<String, Object> map) {
		List<Long> ids = (List<Long>) map.get("ids");
		if (ids == null || ids.size() <= 0) {
			throw new RuntimeException("there is no id to select");
		}
		Class<T> clazz = (Class<T>) map.get("clazz");
		BEGIN();
		SELECT("*");
		FROM(clazz.getAnnotation(Table.class).value());
		MessageFormat mf = new MessageFormat("#'{'ids[{0}]}");
		StringBuilder builder = new StringBuilder();
		for (int i = 0; i < ids.size(); i++) {
			builder.append(mf.format(new Object[] { i }) + ",");
		}
		String cnd = "";
		if (builder.length() > 1) {
			cnd = builder.substring(0, builder.length() - 1);
		}
		WHERE(getIdColmnName(clazz) + " in (" + cnd + ")");
		String target = SQL();
		log.debug(String.format("sql generated by SQLTemplate: \n {}", target));
		return target;
	}

	/**
	 * 根据条件查询
	 * 
	 * @param map
	 * @return
	 */
	public String selectByCondition(Map<String, Object> map) {
		Class<T> clazz = (Class<T>) map.get("clazz");
		Condition cnd = (Condition) map.get("cnd");
		BEGIN();
		SELECT("*");
		FROM(clazz.getAnnotation(Table.class).value());
		MessageFormat mf = new MessageFormat("#'{'cnd[{0}].value}");
		for (int i = 0; i < cnd.size(); i++) {
			ConditionNode conditionNode = cnd.get(i);
			WHERE(getColumn(conditionNode.getKey(), clazz) + " " + conditionNode.getOperator() + " " + mf.format(new Object[] { i }));
			if (cnd.size() > 1 && i < cnd.size() - 1) {
				AND();
			}
		}
		String target = SQL();
		log.debug(String.format("sql generated by SQLTemplate: \n {}", target));
		return target;
	}

	/**
	 * 根据条件计数
	 * 
	 * @param map
	 * @return
	 */
	public String countByCnd(Map<String, Object> map) {
		Class<T> clazz = (Class<T>) map.get("clazz");
		Condition cnd = (Condition) map.get("cnd");
		MessageFormat mf = new MessageFormat("#'{'cnd[{0}].value}");
		BEGIN();
		SELECT("count(" + getIdColmnName(clazz) + ")");
		FROM(clazz.getAnnotation(Table.class).value());
		for (int i = 0; i < cnd.size(); i++) {
			ConditionNode conditionNode = cnd.get(i);
			WHERE(getColumn(conditionNode.getKey(), clazz) + " " + conditionNode.getOperator() + " " + mf.format(new Object[] { i }));
			if (cnd.size() > 1 && i < cnd.size() - 1) {
				AND();
			}
		}
		String target = SQL();
		log.debug(String.format("sql generated by SQLTemplate: \n {}", target));
		return target;
	}

	/**
	 * 分页查询
	 * 
	 * @param map
	 * @return
	 */
	public String selectByPage(Map<String, Object> map) {
		Class<T> clazz = (Class<T>) map.get("clazz");
		OrderBy[] orders = map.get("orders") == null ? null : (OrderBy[]) map.get("orders");
		Condition cnd = map.get("cnd") == null ? null : (Condition) map.get("cnd");
		MessageFormat mf = new MessageFormat("#'{'cnd[{0}].value}");
		int pageSize = (int) map.get("pageSize");
		int page = (int) map.get("page");
		BEGIN();
		SELECT("*");
		FROM(clazz.getAnnotation(Table.class).value());
		for (int i = 0; i < cnd.size(); i++) {
			ConditionNode conditionNode = cnd.get(i);
			WHERE(getColumn(conditionNode.getKey(), clazz) + " " + conditionNode.getOperator() + " " + mf.format(new Object[] { i }));
			if (cnd.size() > 1 && i < cnd.size() - 1) {
				AND();
			}
		}
		for (OrderBy orderBy : orders) {
			ORDER_BY(orderBy.getCol() + " " + orderBy.getOrder());
		}
		String target = SQL() + " limit " + (page - 1) * pageSize + "," + pageSize;
		log.debug(String.format("sql generated by SQLTemplate: \n {}", target));
		return target;
	}

	/**
	 * 根据分页器查询
	 * 
	 * @param map
	 * @return
	 */
	public String selectByPager(Map<String, Object> map) {
		Class<T> clazz = (Class<T>) map.get("clazz");
		OrderBy[] orders = map.get("orders") == null ? null : (OrderBy[]) map.get("orders");
		Condition cnd = map.get("cnd") == null ? null : (Condition) map.get("cnd");
		MessageFormat mf = new MessageFormat("#'{'cnd[{0}].value}");
		Pager pager = (Pager) map.get("pager");
		BEGIN();
		SELECT("*");
		FROM(clazz.getAnnotation(Table.class).value());
		if (cnd != null) {
			for (int i = 0; i < cnd.size(); i++) {
				ConditionNode conditionNode = cnd.get(i);
				WHERE(getColumn(conditionNode.getKey(), clazz) + " " + conditionNode.getOperator() + " " + mf.format(new Object[] { i }));
				if (cnd.size() > 1 && i < cnd.size() - 1) {
					AND();
				}
			}
		}

		for (OrderBy orderBy : orders) {
			ORDER_BY(orderBy.getCol() + " " + orderBy.getOrder());
		}
		String target = SQL() + " limit " + (pager.getPage() - 1) * pager.getPageSize() + "," + pager.getPageSize();
		log.debug(String.format("sql generated by SQLTemplate: \n {}", target));
		return target;
	}

	/**
	 * @param key
	 * @param clazz
	 * @return
	 */
	private String getColumn(String key, Class<T> clazz) {
		for (Field f : Classs.getAllfFields(clazz)) {
			if (StringUtils.equals(f.getName(), key)) {
				if (f.getAnnotation(Column.class) != null) {
					return f.getAnnotation(Column.class).value();
				}
				if (f.getAnnotation(Id.class) != null) {
					String id = f.getAnnotation(Id.class).value();
					return StringUtils.equals("", id) ? f.getName() : id;
				}
			}
		}
		throw new RuntimeException(" '" + clazz.getName() + "' 类没有 '" + key + "' 属性");
	}

}
